<template>
  <div class="root-container">
    <div style="width: 100%; height: calc(100% - 41px); position: relative">
      <div style="width: 100%; height: 100%;" ref="diagramRef"></div>
    </div>
    <xml-editor v-model:open="previewVisible" :modelValue="true" :code="previewCode" @confirm="handleUpdateXml" />
    <div id="workflow-mask-panel"></div>
    <a-tooltip v-model:open="errTooltipVisible" placement="right" :getPopupContainer="() => errEl" trigger="manual"
      :autoAdjustOverflow="true">
      <template #title>
        <span style="color: red; font-weight: bold" v-text="errText"></span>
      </template>
    </a-tooltip>
  </div>
</template>

<script setup>
import { List, message } from "ant-design-vue";
// import { ViewOutlined, FolderOpenOutlined, CheckOutlined, ReloadOutlined, ExportOutlined } from "@ant-design/icons-vue";
import { onMounted, provide, ref, shallowRef, watch } from "vue";
// import { modelApi } from "/@/api/business/flowable/model";
// import BpmnModeler from "bpmn-js/lib/Modeler"
import 'bpmn-js/dist/assets/bpmn-js.css';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
import 'diagram-js-minimap/assets/diagram-js-minimap.css'
// import miniMapModule from 'diagram-js-minimap'
// import flowableDescriptor from "@/assets/flowable/descriptor.json"
// import { useIcon } from "@/components/common/util";
// import Collapsed from "@/components/common/Collapsed.vue";
// import PropertyPanel from '@/components/bpmn/PropertyPanel.vue'
import {
  bpmnModelerKey,
  bpmnSelectedElemKey,
  modelingFieldKey,
  modelingPageKey,
  workflowVerKey
} from "/@/config/app.keys";
import emitter from "/@/event/mitt";
import XmlEditor from "/@/components/common/XmlEditor.vue";
// import ElementRegistry from 'diagram-js/lib/core/ElementRegistry'
import { validate } from './bpmnlint'
// import Canvas from "diagram-js/lib/core/Canvas";
import InitBPMNXml from '/@/assets/bpmn/init.bpmn20.xml?raw'
import { useRoute, useRouter } from "vue-router";
import GridLineModule from 'diagram-js-grid-bg'
import { useModelingPageApi } from "/@/service/modeling/page";
import { useModelingFieldApi } from "/@/service/modeling/field";
import { useVerApi } from "/@/service/workflow/ver";

// import BpmnViewer from 'bpmn-js/lib/NavigatedViewer'
import { CustomViewer as BpmnViewer } from "/@/components/customBpmn";
import ImportModule from 'bpmn-js/lib/import/index'
import DrawModule from 'bpmn-js/lib/draw/index'
import MiniMapModule from 'diagram-js-minimap/dist/index'

import { SmartLoading } from '/@/components/framework/smart-loading';

const route = useRoute()
console.log('route', route, route.name)
const router = useRouter()

const props = defineProps({
  bpmnXml: {
    type: String,
    required: true
  },
  taskList: {
    type: List,
    required: false
  }
});

const loading = ref(false)
const { workflowVer, findVer, updateXml } = useVerApi(loading)
const { modelingFields, findModelingFields } = useModelingFieldApi(loading)
const { pageList, findModulePages } = useModelingPageApi(loading)

provide(workflowVerKey, workflowVer)
provide(modelingFieldKey, modelingFields)
provide(modelingPageKey, pageList)

const diagramRef = ref()
onMounted(initDiagram)

const scale = ref(1)
const bpmnModeler = shallowRef()
provide(bpmnModelerKey, bpmnModeler)
const bpmnSelectedElem = shallowRef()

function initDiagram() {
  bpmnModeler.value = new BpmnViewer({
    container: diagramRef.value,
    additionalModules: [
      ImportModule, MiniMapModule, DrawModule, GridLineModule,
    ],
    minimap: {
      open: false
    },
    textRender: {
      defaultStyle: {
        color: 'red'
      }
    },
  })

  if (props.bpmnXml != '' && typeof (props.bpmnXml) != "undefined") {
    SmartLoading.show()
    importXML(props.bpmnXml)
    SmartLoading.hide()
  }
}

watch(
  () => props.bpmnXml,
  (newVal) => {
    SmartLoading.show()
    if (newVal != '' && typeof (newVal) != "undefined") {
      importXML(newVal)
    }
    SmartLoading.hide()
  },
  {
    immediate: true,
  }
)

async function importXML(xml) {
  try {
    if (!bpmnModeler.value) {
      return
    }
    const result = await bpmnModeler.value.importXML(xml)
    const canvas = bpmnModeler.value.get('canvas')
    if (!canvas) {
      return
    }
    canvas.zoom("fit-viewport", true);
    canvas.zoom(Math.ceil(scale.value / 100));

    const registry = bpmnModeler.value.get("elementRegistry")
    if (!registry) {
      return
    }
    fillColor()
    bpmnSelectedElem.value = registry.find(it => it.type === 'bpmn:Process')
    bpmnModeler.value.on("selection.changed", e => {
      fillColor()
      console.log('element select change', e);
      const selectionArray = e.newSelection
      bpmnSelectedElem.value = selectionArray?.length ? selectionArray[0] : registry.find(it => it.type === 'bpmn:Process')
      emitter.emit('bpmnSelectionChanged', { element: bpmnSelectedElem.value })
    })
    bpmnModeler.value.on("element.changed", e => {
      console.log('element change', e);
      emitter.emit('bpmnElementChanged', { element: e.element })
    })
  } catch (e) {
    console.error(e)
    message.error("导入失败: " + e.message)
  }
}

function fillColor() {
  console.log('开始执行高亮，taskList:', props.taskList);
  const canvas = bpmnModeler.value.get('canvas');
  console.log('canvas 实例:', canvas);
  
  if (!props.taskList || !Array.isArray(props.taskList)) {
    console.warn('taskList 不是有效的数组');
    return;
  }

  const rootElements = bpmnModeler.value.getDefinitions().rootElements;
  if (!rootElements || !rootElements[0]) {
    console.warn('未找到根元素');
    return;
  }

  rootElements[0].flowElements.forEach(n => {
    console.log('处理元素:', { id: n.id, type: n.$type });
    const completeTask = props.taskList.find(m => m.key === n.id);
    const todoTask = props.taskList.find(m => !m.completed);
    const endTask = props.taskList[props.taskList.length - 1];

    if (n.$type === 'bpmn:UserTask') {
      if (completeTask) {
        console.log('添加用户任务高亮:', { id: n.id, completed: completeTask.completed });
        canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
        n.outgoing?.forEach(nn => {
          const targetTask = props.taskList.find(m => m.key === nn.targetRef.id)
          if (targetTask) {
            if (todoTask && completeTask.key === todoTask.key && !todoTask.completed) {
              canvas.addMarker(nn.id, todoTask.completed ? 'highlight' : 'highlight-todo')
              canvas.addMarker(nn.targetRef.id, todoTask.completed ? 'highlight' : 'highlight-todo')
            } else {
              canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
              canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
            }
          }
        })
      }
    }
    // 排他网关
    else if (n.$type === 'bpmn:ExclusiveGateway') {
      if (completeTask) {
        canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
        n.outgoing?.forEach(nn => {
          const targetTask = props.taskList.find(m => m.key === nn.targetRef.id)
          if (targetTask) {
            canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
            canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
          }
        })
      }
    }
    // 并行网关
    else if (n.$type === 'bpmn:ParallelGateway') {
      if (completeTask) {
        canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
        n.outgoing?.forEach(nn => {
          const targetTask = props.taskList.find(m => m.key === nn.targetRef.id)
          if (targetTask) {
            canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
            canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
          }
        })
      }
    }
    else if (n.$type === 'bpmn:StartEvent' || n.$type === 'bpmn:EndEvent') {
      const elementRegistry = bpmnModeler.value.get('elementRegistry');
      const element = elementRegistry.get(n.id);
      if (element) {
        // 添加高亮
        if (n.$type === 'bpmn:StartEvent') {
          canvas.addMarker(n.id, 'highlight');
        } else if (n.$type === 'bpmn:EndEvent' && endTask && endTask.key === n.id && endTask.completed) {
          canvas.addMarker(n.id, 'highlight');
        }
      }
    }
  });
}

const previewCode = ref('')
const previewVisible = ref(false)

async function handlePreviewXml() {
  if (!bpmnModeler.value) {
    return
  }
  SmartLoading.show()
  const { xml } = await bpmnModeler.value.saveXML({ format: true })
  previewCode.value = xml
  previewVisible.value = true
  SmartLoading.hide()
}

function handleUpdateXml(xml) {
  importXML(xml)
  previewVisible.value = false
}

const errTooltipVisible = ref(false)
const errEl = shallowRef()
const errText = ref('')

function validateBpmn() {
  if (!bpmnModeler.value) {
    return false
  }
  const canvas = bpmnModeler.value.get('canvas')
  const root = canvas.getRootElement()
  console.log('root type', root.constructor.name, root)

  const registry = bpmnModeler.value.get("elementRegistry")

  const err = validate(registry, root)
  if (err) {
    if (!err.element) {
      message.error(err.message)
      return false
    }
    errEl.value = document.querySelector(`g.djs-element[data-element-id=${err.element.id}] .djs-visual`)
    errText.value = err.message
    errTooltipVisible.value = true
    return false
  }
  return true
}

function handleReset() {
  if (!bpmnModeler.value || !workflowVer.value) {
    return
  }
  const registry = bpmnModeler.value.get('elementRegistry');
  const process = registry.get(workflowVer.value.key);
  if (!process) {
    return
  }
  const xml = InitBPMNXml
    .replace(/{{PROCESS_ID}}/g, process.id)
    .replace(/{{PROCESS_NAME}}/g, process.businessObject.name)
    .replace(/{{START_EVENT_ID}}/g, 'StartEvent_' + Math.random().toString(36).replace(/0\./g, ''))
  importXML(xml)
}
</script>

<style>
.root-container {
  width: 100%;
  height: 100%;
}

/* BPMN 高亮样式 */
.highlight {
  fill: rgba(82, 196, 26, 0.2) !important;
  stroke: #52c41a !important;
}

.highlight-todo {
  fill: rgba(250, 173, 20, 0.2) !important;
  stroke: #faad14 !important;
}

/* 确保高亮样式能够覆盖 BPMN 默认样式 */
.djs-element .highlight,
.djs-element .highlight-todo {
  stroke-width: 2px !important;
}

/* 连接线高亮 */
.djs-connection .highlight,
.djs-connection .highlight-todo {
  stroke-width: 2px !important;
}

/* 确保高亮样式优先级足够高 */
.djs-element.highlight *,
.djs-element.highlight-todo * {
  fill: inherit !important;
  stroke: inherit !important;
}

/* 保持原始图形可见 */
.djs-element .djs-visual {
  opacity: 1 !important;
}

/* 确保高亮不会完全覆盖原始图形 */
.djs-element .djs-visual > * {
  opacity: 1 !important;
}

/* 调整高亮层的透明度 */
.djs-element .djs-visual > .highlight,
.djs-element .djs-visual > .highlight-todo {
  opacity: 0.2 !important;
}

/* StartEvent 和 EndEvent 的特定样式 */
.djs-element[data-element-id*="StartEvent"] .djs-visual > .highlight,
.djs-element[data-element-id*="EndEvent"] .djs-visual > .highlight,
.djs-element[data-element-id*="StartEvent"] .djs-visual > .highlight-todo,
.djs-element[data-element-id*="EndEvent"] .djs-visual > .highlight-todo {
  rx: 50% !important;
  ry: 50% !important;
  r: 50% !important;
  cx: 50% !important;
  cy: 50% !important;
  opacity: 0.3 !important;
}

/* 确保 StartEvent 和 EndEvent 的图标可见 */
.djs-element[data-element-id*="StartEvent"] .djs-visual > *,
.djs-element[data-element-id*="EndEvent"] .djs-visual > * {
  opacity: 1 !important;
}

/* 为 StartEvent 和 EndEvent 添加圆形遮罩 */
.djs-element[data-element-id*="StartEvent"] .djs-visual,
.djs-element[data-element-id*="EndEvent"] .djs-visual {
  mask: circle(50% at 50% 50%) !important;
  -webkit-mask: circle(50% at 50% 50%) !important;
}

/* 高亮样式 */
:deep(.highlight) {
  fill: rgba(255, 193, 7, 0.2) !important;
  stroke: #ffc107 !important;
  stroke-width: 2px !important;
}

:deep(.highlight-todo) {
  fill: rgba(0, 123, 255, 0.2) !important;
  stroke: #007bff !important;
  stroke-width: 2px !important;
}

/* 开始和结束事件的圆形样式 */
:deep(.djs-visual > rect[width="36"][height="36"]) {
  rx: 18px !important;
  ry: 18px !important;
}

:deep(.djs-visual > circle) {
  r: 18px !important;
  cx: 18px !important;
  cy: 18px !important;
}

/* 确保图标可见 */
:deep(.djs-visual > g) {
  transform: translate(9px, 9px) !important;
}
</style>
